/*
 * Filename : Encounter.java
 * Description : **COMPLETE**
 * History  : Date               Engineer       Description
 *            ---------          ------------   --------------------------------
 *            August 24, 2007      waitek         Initial creation.
 */

package com.lct.eq2.data;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Date;
import java.util.Iterator;
import javax.swing.tree.DefaultMutableTreeNode;

public class Encounter implements Serializable, Comparator {
    static final long serialVersionUID = 5619338131616584255L;
    
    private boolean zonewide;
    private Combatant mainEnemy;
    private Date encounterStarted;
    private Date lastUpdated;
    private Duration zonewideDuration;
    private HashMap combatants;
    private ArrayList enemies;
    private ArrayList allies;
    private String mainCharacter;
    private String zone;
    
    /** Constructor for comparator */
    public Encounter() {
    }
    
    public Encounter(long started, String mainCharacter, boolean zonewide) {
        this.lastUpdated = new Date(started);
        this.encounterStarted = new Date(started);
        this.zonewideDuration = new Duration();
        this.zonewide = zonewide;
        enemies = new ArrayList();
        allies = new ArrayList();
        combatants = new HashMap();
        this.mainCharacter = mainCharacter;
    }
    
    /** Creates a new instance of Encounter */
    public Encounter(long started, String mainCharacter, String currentZone) {
        this.zonewide = false;
        this.zone = currentZone;
        this.lastUpdated = new Date(started);
        enemies = new ArrayList();
        allies = new ArrayList();
        this.encounterStarted = new Date(started);
        combatants = new HashMap();
        this.mainCharacter = mainCharacter;
    }
    
    public Encounter(Attack attack, String mainCharacter, String currentZone, boolean propagated, String friendName) {
        this.zonewide = false;
        this.zone = currentZone;
        enemies = new ArrayList();
        allies = new ArrayList();
        this.encounterStarted = new Date(attack.getTimeStamp());
        //System.out.println("New encounter: " + this.encounterStarted);
        combatants = new HashMap();
        this.mainCharacter = mainCharacter;
        this.addAttack(attack, propagated, friendName);
    }
    
    public void setDuration(Duration duration) {
        if (this.zonewide) {
            this.zonewideDuration = duration;
        }
    }
    
    public void addToZonewideDuration(Duration duration) {
        this.zonewideDuration.addDuration(duration);
    }
    
    public void addEncounter(Encounter enc, Duration duration) {
        ArrayList newEnemies = enc.getEnemies();
        ArrayList newAllies = enc.getAllies();
        
        for (int x = 0; x < newAllies.size(); x++) {
            String ally = (String)newAllies.get(x);
            if (!this.allies.contains(ally)) {
                this.allies.add(ally);
            }
        }
        
        for (int x = 0; x < newEnemies.size(); x++) {
            String ally = (String)newEnemies.get(x);
            if (!this.enemies.contains(ally)) {
                this.enemies.add(ally);
            }
        }
        
        ArrayList newCombatants = enc.getCombatants();
        
        String newZone = enc.getZone();
        this.zone = newZone;
            this.getEncounterDuration().addDuration(duration);
       
        for (int x = 0; x < newCombatants.size(); x++) {
            Combatant c = (Combatant)newCombatants.get(x);
            //System.out.println(c.getName());
            ArrayList attacks = c.getAttacks();

            for (int y = 0; y < attacks.size(); y++) {
                Attack a = (Attack)attacks.get(y);
                //System.out.println("    Attack date = " + new Date(a.getTimeStamp()));
                //System.out.println("    Encounter::addEncounter() - attack: " 
                //        + a.getAttacker() + ", " + a.getTarget() + ", " + a.getAmountOfAttackDamage() + ", " 
                //        + a.getAttackDirectionRelativeToCombatant(c));
                this.addAttack(a, false, null);
            }
        }
    }
    
    public ArrayList getAllies() {
        return this.allies;
    }
    
    public Combatant getAssumedMainTank() {
        Combatant rval = null;
        long swings = 0;
        for (Iterator iter = combatants.keySet().iterator(); iter.hasNext();) {
            String combatantName = (String)iter.next();
            if (this.allies.contains(combatantName)) {
                Combatant combatant = this.getCombatant(combatantName);
                long tmp = combatant.getMeleeSwings();
                if (tmp > swings) {
                    swings = tmp;
                    rval = combatant;
                }
            }
        }
        
        //System.out.println("Assumed MT is " + rval.getName());
        if (rval == null) {
            rval = new Combatant();
            rval.setName("Unknown");
        }
        return rval;
    }
    
    public void addAttack(Attack attack, boolean propagated, String friendName) {
//        if (attack.getAttackName().equalsIgnoreCase("brutal inquiry") && attack.getTarget().equalsIgnoreCase("yiix") && attack.getAmountOfAttackDamage() > 0) {
//            boolean stop = true;
//        }
        if (!attack.isHeal()) {
            this.lastUpdated = new Date(attack.getTimeStamp());
            //System.out.println("Encounter updated at: " + this.lastUpdated);
        }
        
        if (propagated) {
            if (friendName != null && !this.allies.contains(friendName)) {
                this.allies.add(friendName);
            }
        }
        
        String attacker = attack.getAttacker();
        String target = attack.getTarget();

        if (!attacker.equalsIgnoreCase(target)) {
            if (!this.combatants.containsKey(attacker)) {
                //System.out.println("Encounter::addAttack() - Attacker Combatant not found, inserting new.");
                this.addAttackerCombatant(attack);
            } else {
                //System.out.println("Encounter::addAttack() - Attacker Combatant found, updating.");
                Combatant combatant = (Combatant)this.combatants.get(attacker);
                attack.setAttackDirection(Attack.ATTACK_TYPE_OUTGOING);
                combatant.setEncounterDuration(this.getEncounterDuration());
                combatant.addAttack(attack, zonewide);
                this.combatants.put(combatant.getName(), combatant);
            }

            if (!this.combatants.containsKey(target)) {
                //System.out.println("Encounter::addAttack() - Target Combatant not found, inserting new.");
                if (attack.isHeal() && !attack.getAttacker().equalsIgnoreCase(attack.getTarget())) {
                    attack.setTrueSelf(true);
                }
                this.addTargetCombatant(attack);
            } else {
                //System.out.println("Encounter::addAttack() - Target Combatant found, updating.");
                Combatant combatant = (Combatant)this.combatants.get(target);
                attack.setAttackDirection(Attack.ATTACK_TYPE_INCOMING);
                combatant.setEncounterDuration(this.getEncounterDuration());
                combatant.addAttack(attack, zonewide);
                if (attack.isMelee()) {
                    combatant.addMeleeSwing();
                }
                this.combatants.put(combatant.getName(), combatant);
            }
        } else {
            if (!this.combatants.containsKey(attacker)) {
                //System.out.println("Encounter::addAttack() - Attacker Combatant not found, inserting new.");
                this.addAttackerCombatant(attack);
            } else {
                //System.out.println("Encounter::addAttack() - Target Combatant found, updating.");
                Combatant combatant = (Combatant)this.combatants.get(target);
                attack.setAttackDirection(Attack.ATTACK_TYPE_SELF);
                combatant.setEncounterDuration(this.getEncounterDuration());
                combatant.addAttack(attack, zonewide);
                this.combatants.put(combatant.getName(), combatant);
            }
        }

        if (!attack.isAvoided()) {
            if ((!this.enemies.contains(target)) && 
                    (this.mainCharacter.equalsIgnoreCase(attacker) || (friendName != null && friendName.equalsIgnoreCase(attacker))) && 
                    (!attack.isHeal()) && (!this.allies.contains(target))) {
                this.enemies.add(target);
            }

            if ((this.mainCharacter.equalsIgnoreCase(target) || (friendName != null && friendName.equalsIgnoreCase(target))) && 
                    (!attack.isHeal()) && (!this.enemies.contains(attacker)) && (!this.allies.contains(attacker))) {
                this.enemies.add(attacker);
            }

            if ((this.enemies.contains(target)) && (!this.allies.contains(attacker)) &&
                    (!attack.isHeal()) && (!this.enemies.contains(attacker))) {
                this.allies.add(attacker);
            }

            if ((!attack.isHeal()) && 
                    ((this.mainEnemy == null) || (!this.getMainEnemy().equalsIgnoreCase(target))) && (this.enemies.contains(target))) {
                    this.setMainEnemy(target);
            }
            
            //System.out.println("attacker = " + attacker + ", target = " + target + ", type = " + attack.getAttackType());
            if (attack.isHeal() && !attack.getAttackName().equalsIgnoreCase("inquisition")) {
                if ((target.equalsIgnoreCase(this.mainCharacter)) && (!allies.contains(attacker))) {
                    this.allies.add(attacker);
                } else if (allies.contains(attacker) && (!allies.contains(target) && !enemies.contains(target))) {
                    this.allies.add(target);
                } else if (allies.contains(target) && (!allies.contains(attacker) && !enemies.contains(attacker))) {
                    this.allies.add(attacker);
                } else if (attacker.equalsIgnoreCase(this.mainCharacter) && !enemies.contains(target)) {
                    this.allies.add(target);
                }
            }
        }
    }
    
    long healTotal = 0;
    public void debugAttack(Attack attack) {
        //DecimalFormat df = new DecimalFormat("#,###,###");
        //healTotal += attack.getAmountOfAttackDamage();
        //System.out.println("Encounter::debugAttack() - Total healing so far: " + df.format(healTotal));
        System.out.println("Encounter::debugAttack() - " + attack.getAttacker() + " dealt " 
                        + attack.getAmountOfAttackDamage() + " damage to " + attack.getTarget()
                        + " with " + attack.getAttackName() + ", " + attack.getAttackType() 
                        + " " + attack.getAttackClass() + " damage.");
    }
    
    public boolean isZonewide() {
        return this.zonewide;
    }
    
    public boolean isAlly(String combatantName) {
        boolean rval = false;
        if (combatantName.contains("'s")) {
            combatantName = combatantName.substring(0, combatantName.indexOf("'s"));
        }
        if (combatantName.equalsIgnoreCase(this.mainCharacter) || this.allies.contains(combatantName)) {
            rval = true;
        } else if (this.enemies.contains(combatantName)) {
            rval = false;
        }
        return rval;
    }
    
    private boolean titleSet = false;
    private String title = null;
    public void setTitle(String title) {
        this.title = title;
        this.titleSet = true;
    }
    
    @Override
    public String toString() {
        String rval;
        if (!this.zonewide) {
            if (!titleSet) {
                rval =  this.getMainEnemy();
            } else {
                rval = this.title;
            }
        } else {
            rval = "Zonewide";
        }
        return rval;
    }
    
    public ArrayList getEnemies() {
        return this.enemies;
    }
    
    public void setMainEnemy(String mainEnemy) {
        Combatant enemy = (Combatant)this.combatants.get(mainEnemy);
        this.mainEnemy = enemy;
    }
    
    public String getMainEnemy() {
        String rval = "Encounter";
        try {
            rval = this.mainEnemy.getName();
        } catch (NullPointerException e) {
            rval = "Encounter";
            //e.printStackTrace();
        }
        return rval;
    }
    
    public Combatant getMainEnemyCombatant() {
        return this.mainEnemy;
    }
    
    private void addAttackerCombatant(Attack attack) {
        Combatant attacker = new Combatant();

        attacker.setName(attack.getAttacker());
        attacker.addAttack(attack, zonewide);
        
        attacker.setEncounterDuration(this.getEncounterDuration());
        
        this.combatants.put(attacker.getName(), attacker);
        if (!attack.isHeal()) {
            this.lastUpdated = new Date(attack.getTimeStamp());
        }
    }
    
    private void addTargetCombatant(Attack attack) {
        Combatant target = new Combatant();
        String _target = attack.getTarget();
        if (_target.equalsIgnoreCase("you")) {
            attack.setTarget(this.mainCharacter);
        }

        target.setName(_target);
        target.addAttack(attack, zonewide);
        target.setEncounterDuration(this.getEncounterDuration());
        this.combatants.put(target.getName(), target);
        this.lastUpdated = new Date(attack.getTimeStamp());
    }
    
    public ArrayList<Combatant> getCombatants() {
        ArrayList<Combatant> rval = new ArrayList<Combatant>();
        Iterator iter = this.combatants.keySet().iterator();
        while (iter.hasNext()) {
            Combatant tmp = (Combatant)this.combatants.get(iter.next());
            //tmp.setEncounterDuration(this.getEncounterDuration());
            rval.add(tmp);
        }
        return rval;
    }
    
    public Combatant getCombatant(String name) {
        Combatant rval = (Combatant)this.combatants.get(name);
        return rval;
    }
    
    public long getSecondsSinceLastUpdated(LogEvent event) {
        long secLong = (this.lastUpdated.getTime() / 1000);
        long updatedSec = (event.getTimeStamp() / 1000);
        return (updatedSec - secLong);
    }
    
    public void setLastUpdated(long timeStamp) {
        try {
            if (this.lastUpdated == null) {
                System.out.println("lastUpdated is null");
            } else {
                this.lastUpdated.setTime(timeStamp);
                if (this.zonewide) {
                    System.out.println("zonewide");
                }
            }
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
    }
    
    public void setZone(String zone) {
        this.zone = zone;
    }
    
    public String getZone() {
        return this.zone;
    }
    
    public Date getStartAsDate() {
        return this.encounterStarted;
    }
    
    public Date getLastUpdated() {
        return this.lastUpdated;
    }
    
    public Duration getEncounterDuration() {
        Duration rval = null;
        if (!this.zonewide) {
            rval = new Duration();
            //System.out.println("Encounter started: " + this.encounterStarted);
            //System.out.println("Encounter ended: " + this.lastUpdated);
            rval.setDurationStartAsDate(this.encounterStarted);
            rval.setDurationEndAsDate(this.lastUpdated);
        } else {
            rval = this.zonewideDuration;
        }
        return rval;
    }
    
    public int compare(Object o1, Object o2) {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode)o1;
        Encounter e1 = (Encounter)node.getUserObject();
        node = (DefaultMutableTreeNode)o2;
        Encounter e2 = (Encounter)node.getUserObject();
        int rval = 0;
        
        if (e1.getLastUpdated().compareTo(e2.getLastUpdated()) > 0) {
            rval = 1;
        } else if (e1.getLastUpdated().compareTo(e2.getLastUpdated()) < 0) {
            rval = -1;
        } else {
            rval = 0;
        }
        
        if (e1.zonewide) {
            rval = 1;
        }
        if (e2.zonewide) {
            rval = -1;
        }
        
        return rval;
    }
}
